package simtester.server

class QueryController {

    def responsetime() {
        TestRun tr = TestRun.get(params.id.toLong())
        boolean done = false
        if (tr.status == 'completed') {
            done = true
        }

        def minimum = 0
        if (params.min) {
            minimum = params.min.toLong()
        }
        Date minDate = new Date(minimum)
        
        def userminimum = 0
        if (params.userMin) {
            userminimum = params.userMin.toLong()
        }
        Date userMinDate = new Date(userminimum)
        
        int userCount = 0
        if (params.users) {
            userCount = params.users.toInteger()
        }
        
        def entries = ResultEntry.withCriteria {
            eq 'testrun', tr
            ge 'timestamp', minDate
            order 'timestamp'
        }
        
        def responses = []
        entries.each {
            if (it.response) {
                responses << [resp: it.response, time: it.timestamp.time]
            }
        }

        def users = []
        def iters = []
        // Type 0 = start
        // Type 1 = current
        // Type 2 = end
        tr.iterations.each {
            if (it.startDate >= userMinDate) {
                iters << [type: 0, time: it.startDate.time]
            }
            if (it.endDate && it.endDate >= userMinDate) {
                iters << [type: 2, time: it.endDate.time]
            }
        }

        long latestTime
        if (iters.size() == 0 && !done) {
            users << [count: userCount, time: System.currentTimeMillis()]
        } else if (iters.size() == 1) {
            latestTime = iters[0].time
        } else if (iters.size() > 1) {
            long start, end
            
            start = end = iters[0].time
            
            iters.each {
                start = Math.min(start, it.time)
                end   = Math.max(end, it.time)
            }

            start += 500
            if (start > end) {
                start = (start + end) / 2
            }
            (start..end).step(2000) {
                iters << [type: 1, time: it]
            }
            iters = iters.sort { it.time }
            
            latestTime = end
        }

        iters.each {
            if (it.type == 0) {
                userCount++
            } else if (it.type == 2) {
                userCount--
                if (userCount < 0)
                    userCount = 0
            } else {
                users << [count: userCount, time: it.time]
            }
        }

        render (contentType:'text/json') {
            [done: done, responses: responses, users: users, userCount: userCount, userLatestTime: latestTime]
        }
    }
    
    def testrunResults() {
        TestRun tr = TestRun.get(params.id.toLong())
        
        boolean done = false
        if (tr.status == 'completed') {
            done = true
        }

        boolean isTestSuite = false
        if (tr.usersPerTest || tr.testcaseIds) {
            isTestSuite = true
        }
        
        boolean queryErrors = false
        if (params.type == 'failures') {
            queryErrors = true
        }

        def count = 10
        def start = params.num.toInteger()
        def end = start + count
        def testcaseId = params.tc.toLong()
        int user = params.user.toInteger()
        
        def results = []
        def failures = []
        def total
        def progress = []
        if (isTestSuite) {
            if (queryErrors) {
                results = tr.iterations.findAll {
                    it.testcase == testcaseId &&
                    it.status   == 'failed'
                }
                
                end = end-1
                if (end >= results.size()-1) {
                    end = results.size()
                }

                results = results[(start-1)..(end-1)]
                
                total = tr.iterations.count {
                    it.testcase == testcaseId &&
                    it.status == 'failed'
                }
            } else {
                if (params.trtype == 'Load') {
                    results = tr.iterations.findAll {
                        it.testcase == testcaseId &&
                        it.num      >= start &&
                        it.num      <  end
                    }
                    
                    total = tr.iterations.count {
                        it.testcase == testcaseId
                    }
                } else {
                    results = tr.iterations.findAll {
                        it.testcase == testcaseId &&
                        it.user     == user &&
                        it.num      >= start &&
                        it.num      <  end
                    }
                    
                    total = tr.iterations.count {
                        it.testcase == testcaseId &&
                        it.user     == user
                    }
                }
                
                
            }

            if (tr.type == 'GUI') {
                progress = tr.usersPerTest.collect { tcId, numUsers ->
                    long tcIdLong = tcId.toLong()
                    int errs = tr.iterations.count {
                        it.testcase == tcIdLong &&
                        it.status   == 'failed'
                    }
                    
                    int prog = getProgress(tr, numUsers, tcIdLong);
                    
                    [tc: tcId, errs: errs, prog: prog]
                }
            } else {
                progress = tr.testcaseIds.collect { tcId ->
                    int errs = tr.iterations.count {
                        it.testcase == tcId &&
                        it.status   == 'failed'
                    }
                    
                    int prog = getProgress(tr, 1, tcId)
                    
                    [tc: tcId, errs: errs, prog: prog]
                }
            }
        } else {
            if (queryErrors) {
                results = tr.iterations.findAll {
                    it.status == 'failed' &&
                    it.num    >= start &&
                    it.num    <  end
                }
                
                total = tr.iterations.count { it.status == 'failed' }
            } else {
                results = tr.iterations.findAll {
                    it.user == user &&
                    it.num  >= start &&
                    it.num  <  end
                }
                
                total = tr.iterations.count { it.user == user }
            }
            
            int errs = tr.iterations.count {
                it.status == 'failed'
            }

            int prog = getProgress(tr, tr.users)
            progress << [tc: tr.testcase.id.toString(), errs: errs, prog: prog]
        }

        results = results.collect {
            [testcase: it.testcase, user: it.user, num: it.num, status: it.status, dur: it.duration().toString()]
        }
        
        def testrun = [done: done, duration: tr.duration.toString()]
        if (tr.endDate) {
            testrun.endDate = tr.endDate.time
        }

        render (contentType:'text/json') {
            [total: total, results: results, tr: testrun, progress: progress]
        }
    }
    
    def outputs() {
        TestRun tr = TestRun.get(params.id.toLong())
        
        boolean done = false
        if (tr.status == 'completed') {
            done = true
        }

        boolean isTestSuite = false
        if (tr.usersPerTest || tr.testcaseIds) {
            isTestSuite = true
        }
        
        def results
        if (isTestSuite) {
            results = tr.findResults(testcase: params.tc.toLong(), user: params.user.toInteger(), iter: params.iter.toInteger(), min: params.min.toLong())
        } else {
            results = tr.findResults(user: params.user.toInteger(), iter: params.iter.toInteger(), min: params.min.toLong())
        }
        def outputs = []
        def highestId
        if (results.size() > 0) {
            outputs = results.collect { it.output }
            highestId = results[-1].time
        }

        render (contentType:'text/json') {
            [outputs: outputs, highestId: highestId, done: done]
        }
    }
    
    def testcase() {
        TestCase tc = TestCase.get(params.id.toLong())
        
        render (contentType:'text/json') {
            [name: tc.name, script: tc.script]
        }
    }
    
    def testcases() {
        def type = params.type
        if (params.id) {
            type = TestSuite.get(params.id.toLong()).type
        }
        
        def catlist = Category.list(sort: 'name').collect { it.name }
        
        def team = User.findByUsername(session.user).team
        def testcases = team.testcases.findAll { it.type == type }
        testcases = testcases.sort { it.name }
        
        testcases = testcases.collect { tc ->
            def cats = catlist.collect { name ->
                if (tc.findCatValue(name)) {
                    tc.findCatValue(name)
                } else {
                    ' '
                }
            }
            [id: tc.id.toString(), name: tc.name, type: type, cats: cats, owner: tc.owner.username, date: tc.dateCreated.time]
        }
        
        render (contentType:'text/json') {
            testcases
        }
    }
    
    def calcResults() {
        TestRun tr = TestRun.get(params.id.toLong())
        
        def results = [:]
        def stats = params.stats.tokenize(',')
        
        if (stats.contains('rps')) {
            def rpsData = getRpsData(tr)
            results.rps     = rpsData.rps
            results.peakRps = rpsData.peakRps
        }
        
        if (stats.contains('avgresp')) {
            results.avgresp = getAvgRespData(tr)
        }
        
        render (contentType:'text/json') {
            results
        }
    }
    
    def summary() {
        TestRun tr = TestRun.get(params.id.toLong())
        
        int total = tr.iterations.size()
        int passed = tr.iterations.count { it.status == 'success' }

        def requests = ResultEntry.withCriteria {
            eq 'testrun', tr
        }
        requests = requests.count { it.response }
        
        def peakResp = ResultEntry.withCriteria {
            eq 'testrun', tr
            projections {
                max 'response'
            }
        }
        
        render (contentType:'text/json') {
            [total: total, passed: passed, requests: requests, peakResp: peakResp[0]]
        }
    }
    
    def testsuite() {
        def suite = TestSuite.get(params.id.toLong())
        def catlist = Category.list(sort: 'name').collect { it.name }

        def tclist = suite.testcases.collect { tc ->
            def cats = []
            catlist.each {
                def val = tc.findCatValue(it)
                if (val) {
                    cats << val
                } else {
                    cats << ' '
                }
            }

            [id: tc.id.toString(), name: tc.name, type: tc.type, cats: cats, owner: tc.owner.username, date: tc.dateCreated.time]
        }
        
        tclist = tclist.sort { it.name }
        
        render (contentType:'text/json') {
            [name: suite.name, tclist: tclist]
        }
    }


    private def getRpsData(tr) {
        def results = ResultEntry.withCriteria {
            eq 'testrun', tr
            order 'timestamp'
        }

        def entries = []
        results.each {
            if (it.response) {
                entries << it.timestamp.time
            }
        }

        def data = [:]
        data.rps = []
        data.peakRps = 0
        
        long current = entries[0]
        int counter = 0
        for (int i = 0; i < entries.size(); i++) {
            if (entries[i] <= current) {
                counter++;
            } else {
                data.rps << [current, counter]
                
                if (counter > data.peakRps) {
                    data.peakRps = counter
                }
                
                i--
                counter = 0
                current += 1000
            }
        }
        
        if (counter != 0) {
            data.rps << [entries[-1], counter]
        }
        
        return data
    }
    
    private def getAvgRespData(tr) {
        def results = ResultEntry.withCriteria {
            eq 'testrun', tr
            order 'timestamp'
        }

        def entries = []
        results.each {
            if (it.response) {
                entries << [time: it.timestamp.time, resp: it.response]
            }
        }

        def avgresp = []
        
        long current = entries[0].time
        int counter = 0
        int respCounter = 0
        for (int i = 0; i < entries.size(); i++) {
            if (entries[i].time <= current) {
                respCounter += entries[i].resp
                counter++
            } else {
                if (counter != 0) {
                    avgresp << [current, respCounter / counter]
                }
                i--
                counter = 0
                respCounter = 0
                current += 1000
            }
        }
        
        if (counter != 0) {
            avgresp << [entries[-1].time, respCounter / counter]
        }
        
        return avgresp
    }
    
    private int getProgress(tr, numUsers, tcId = null) {
        int prog = 0
        if (tr.testDuration) {
            Iteration firstIter
            if (tcId) {
                firstIter = tr.iterations.find {
                    it.testcase == tcId &&
                    it.user == 1 &&
                    it.num  == 1
                }
            } else {
                firstIter = tr.iterations.find {
                    it.user == 1 &&
                    it.num  == 1
                }
            }

            if (firstIter) {
                long start = firstIter.startDate.time
                long current = System.currentTimeMillis() - start
                long end = tr.testDuration

                prog = (current / end) * 100
            } else {
                prog = 0
            }
        } else {
            int total = tr.repeats * numUsers
            int current
            if (tcId) {
                current = tr.iterations.count {
                    it.testcase == tcId &&
                    it.status != 'running'
                }
            } else {
                current = tr.iterations.count {
                    it.status != 'running'
                }
            }

            prog = (current / total) * 100
        }
        
        if (prog > 100) {
            prog = 100
        }
        
        return prog
    }
}
